%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%  Copyright by Wenliang Du.                                       %%
%%  This work is licensed under the Creative Commons                %%
%%  Attribution-NonCommercial-ShareAlike 4.0 International License. %%
%%  To view a copy of this license, visit                           %%
%%  http://creativecommons.org/licenses/by-nc-sa/4.0/.              %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\newcommand{\commonfolder}{../../common-files}
\input{\commonfolder/header}
\input{\commonfolder/copyright}

\lhead{\bfseries SEED 实验 -- IP/ICMP 攻击实验}
\newcommand{\ipFigs}{./Figs}

\begin{document}


\begin{center}
{\LARGE IP/ICMP 攻击实验}
\end{center}

\seedlabcopyright{2020}

% *********************************************
% SECTION
% ********************************************* 
\section{概述}

本实验旨在让学生亲身体验IP层的各种攻击。虽然一些攻击可能已经失效，但其底层技术却非常普遍且重要。学生应学习这些攻击技术，以便在设计或分析网络协议时，能够意识到攻击者可以对协议做什么。此外，由于IP分片的复杂性，伪造分片后的IP包并不容易。构造伪造的IP分片是培养学生伪造数据包技能的良好实践，这对于网络安全至关重要。我们将使用Scapy进行数据包伪造。本实验涵盖以下部分：

\begin{itemize}[noitemsep]
    \item IP协议和ICMP协议
    \item IP分片及其相关攻击
    \item ICMP重定向攻击
    \item 路由
\end{itemize}

\paragraph{视频}
有关IP协议及IP层攻击的详细内容可以在以下章节中找到：

\begin{itemize}
    \item SEED Lecture 第4节，\seedisvideo
\end{itemize}

\paragraph{实验环境} \seedenvironmentC


% *********************************************
% SECTION
% ********************************************* 
\section{使用容器设置环境}

在本实验中，我们需要多个机器。实验环境的搭建如图~\ref{ip:fig:labsetup}所示。我们将使用容器来设置此环境。

\begin{figure}[htb]
    \centering
    \includegraphics[width=0.8\textwidth]{\commonfolder/Figs/TwoLANs.pdf}
    \caption{实验环境搭建}
    \label{ip:fig:labsetup}
\end{figure}

% -------------------------------------------
% SUBSECTION
% -------------------------------------------
\subsection{容器设置和命令}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input{\commonfolder/container/setup}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% -------------------------------------------
% SUBSECTION
% -------------------------------------------
\subsection{攻击容器简介}

在本实验中，我们既可以使用VM也可以使用攻击容器作为攻击机。如果你查看Docker Compose文件，你会发现攻击容器与其它容器的配置有所不同。以下是这些差异：

\begin{itemize}
    \item \textit{共享文件夹}：当我们使用攻击容器发起攻击时，需要将攻击代码放入攻击容器内。
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    \input{\commonfolder/container/volumes}
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    \item \textit{特权模式}：
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    \input{\commonfolder/container/privileged_mode}
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\end{itemize}

% *********************************************
% SECTION
% ********************************************* 
\section{任务1：IP分片}

我们将仅使用两个容器进行此任务，攻击容器（\texttt{10.9.0.105}）和受害者容器（\texttt{10.9.0.5}）。

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务1.a：执行IP分片}

在本任务中，学生需要构造一个UDP数据包并将其发送到一个UDP服务器。可以在受害者容器内使用 \texttt{"nc -lu -p 9090"} 命令启动一个UDP服务。而不是构建单一的IP数据包，学生需要将该数据包分割成3个片段，每个包含32字节的数据（第一个片段包含8字节的UDP头部以及32字节的数据）。如果操作正确无误，服务器将显示总共96字节的数据。

以下是一个构造第一个片段的示例代码：

\begin{lstlisting}[language=python]
#!/usr/bin/env python3
from scapy.all import *

# 构造IP头信息
ip = IP(src="1.2.3.4", dst="10.9.0.5")
ip.id    = 1000  # 标识符
ip.frag  = 0     # 此IP分片的偏移量
ip.flags = 1     # 标志位
ip.proto = 17    # 对应UDP

# 构造UDP头部
udp = UDP(sport=7070, dport=9090)
udp.len  = 200   # 这应该是所有分片的总长度

# 构造载荷数据
payload = 'A' * 80    # 在第一个片段中放入80字节的数据

# 构造整个数据包并发送出去
pkt = ip/udp/payload  # 对其他片段，我们应该使用ip/payload构造
pkt[UDP].chksum = 0   # 将校验和字段设为零
send(pkt, verbose=0)
\end{lstlisting}

需要注意的是，需要正确设置UDP的校验和字段。如果我们不设置此字段，Scapy将为我们计算校验和，但仅基于第一个片段的数据，这是错误的。如果我们将校验和字段设为零，则Scapy会忽略它。此外，在校验和字段看到零时，接收方不会验证UDP校验和，因为UDP中的校验和验证是可选的。

如果你使用Wireshark观察流量，请注意默认情况下，Wireshark会在最后一个分片包中重组片段并以完整的IP/UDP数据包显示。要改变这种行为，需要在Wireshark首选项中禁用IP片段重组。点击以下菜单序列：\texttt{编辑} $\rightarrow$ \texttt{首选项}; 点击 \texttt{协议} 下拉菜单，找到并点击 \texttt{IPv4}。取消勾选 \texttt{"重组分片的IPv4数据包"} 选项。

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务1.b：具有重叠内容的IP片段}

与任务1.a类似，学生也需要构造3个片段将数据发送到UDP服务器。每个片段的大小由学生自行决定。此任务的目标是创建重叠的片段。特别是前两个片段应有重叠部分。请使用实验来展示当发生重叠时会发生什么情况，请尝试以下重叠场景分别进行：

\begin{itemize}
    \item 第一个片段末尾和第二个片段开头应该有 \texttt{K} 字节的重叠，即第一个片段最后 \texttt{K} 个字节的数据应与第二个片段前 \texttt{K} 个字节的数据具有相同的偏移量。\texttt{K} 的值由学生决定（\texttt{K} 应大于零且小于任一分片的大小）。在报告中，学生们应该说明他们的 \texttt{K} 值。

    \item 第二片段完全包含在第一个片段内。第二片段的大小必须小于第一个片段（它们不能相等）。
\end{itemize}

请尝试两种不同的顺序：(1) 先发送第一个片段和 (2) 先发送第二个片段，请报告结果是否相同。

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务1.c：发送超大IP数据包}

我们知道，IP数据包的最大尺寸为 $2^{16}$ 字节，因为IP头部中的长度字段只有16位。然而，通过使用IP分片技术，我们可以创建一个超过此限制的IP数据包。请构造这样的数据包并将其发送到UDP服务器，并观察服务器对此情况的响应。请报告你的观察结果。

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务1.d：发送不完整的IP数据包}

在本任务中，我们将对受害者容器发起拒绝服务攻击。在此攻击中，攻击者将向受害者发送大量不完整IP数据包，即这些数据包仅包含IP分片但缺少某些片段。所有这些不完整的IP数据包都会留在内核中，直到超时为止。理论上这可能会导致内核分配大量的内存。在过去，这种攻击会导致服务器拒绝服务。请尝试此攻击并描述你的观察结果。

% *********************************************
% SECTION
% ********************************************* 
\section{任务2：ICMP重定向攻击}

ICMP重定向是一种路由器向IP数据包的发送者发送的错误消息。当路由器认为一个数据包被错误路由时，它会通知发送者使用不同的路由器来发送后续的数据包。ICMP重定向可以被攻击者用来改变受害者的路由。

本任务的目标是针对受害者发起ICMP重定向攻击，使得当受害者向 \texttt{192.168.60.5} 发送数据包时，它将使用恶意路由器容器（\texttt{10.9.0.111}）作为其路由器。由于恶意路由器由攻击者控制，攻击者可以拦截这些数据包、进行修改，并重新发送出去。这是一种中间人（MITM）攻击形式。此任务分为两个子任务：ICMP重定向攻击和MITM攻击。

% -------------------------------------------
% SUBSECTION
% -------------------------------------------
\subsection{任务2.a：发起ICMP重定向攻击}

在Ubuntu操作系统中，已有一项针对ICMP重定向攻击的防御措施。在Compose文件中，我们已经通过配置受害者容器来接受ICMP重定向消息而关闭了此防御。

\begin{lstlisting}
// 在docker-compose.yml
sysctls:
      - net.ipv4.conf.all.accept_redirects=1

// 要开启保护，请将其值设置为0
# sysctl net.ipv4.conf.all.accept_redirects=0
\end{lstlisting}

对于此任务，我们将从攻击容器针对受害者发起攻击。在当前的配置中，受害者将使用路由器容器（\texttt{192.168.60.11}）作为其路由器来访问 \texttt{192.168.60.0/24} 网络。如果我们对受害者的IP路由进行操作，可以看到以下内容：

\begin{lstlisting}
# ip route
default via 10.9.0.1 dev eth0 
10.9.0.0/24 dev eth0 proto kernel scope link src 10.9.0.5 
192.168.60.0/24 via (*@\textbf{10.9.0.11}@*) dev eth0
\end{lstlisting}

\paragraph{代码框架} 提供了以下的代码框架，其中一些关键参数留空。学生应在标有 \texttt{@@@@} 的位置填写适当的值。

\begin{lstlisting}[language=python]
#!/usr/bin/python3

from scapy.all import *

ip = IP(src = @@@@,  dst = @@@@)
icmp = ICMP(type=@@@@, code=@@@@)
icmp.gw = @@@@

# 封装的IP数据包应是触发重定向消息的数据包。
ip2 = IP(src = @@@@, dst = @@@@)
send(ip/icmp/ip2/ICMP());
\end{lstlisting}

\paragraph{验证} ICMP重定向消息不会影响路由表，而是会影响路由缓存。路由缓存中的条目会覆盖路由表中的条目，直到这些条目过期。要显示和清空缓存内容，请使用以下命令：

\begin{lstlisting}
// 显示路由缓存
# ip route show cache
192.168.60.5 via 10.9.0.111 dev eth0
    cache <redirected> expires 296sec

// 清空路由缓存
# ip route flush cache
\end{lstlisting}

请在受害机器上执行traceroute，并查看包是否被重定向。

\begin{lstlisting}
# mtr -n 192.168.60.5
\end{lstlisting}

\paragraph{奇怪的问题} 在开发此实验时，我们观察到容器环境中存在一个奇怪的问题，而虚拟机（VM）环境不存在该问题。
如果在攻击过程中受害者机器不发送ICMP数据包，伪造重定向包的攻击将永远不能成功。而在虚拟机环境下则不会出现这种现象。此外，在重定向包中的 \texttt{ip2} 必须与受害机器当前发送的数据包类型和目的IP地址相匹配（例如，对于ICMP包为ICMP，UDP包为UDP等）。

似乎操作系统内核在接收ICMP重定向数据包之前会进行某种形式的合理性检查。我们尚未弄清楚是什么原因导致了这个问题，并且为何虚拟机没有这些限制。
这在SEED实验中是一个悬而未决的问题，我们鼓励学生帮助我们解决这个问题。我们建议讲师如果学生确实解决了此问题，则可以给予额外积分。

在找到禁用这种检查机制的方法之前，在发起攻击时我们需要对受害机器上的 \texttt{192.168.60.5} 主机进行 \texttt{ping} 操作。

\paragraph{问题} 在成功实施攻击后，请执行以下实验，并观察你的攻击是否仍然成功。请解释你的观察结果：

\begin{itemize}
    \item 问题1：你能否使用ICMP重定向攻击将目标引导至远程机器？即，\texttt{icmp.gw} 赋予的IP地址是本地局域网之外的一台计算机。
    请展示实验结果并解释你观察到的现象。

    \item 问题2：你能否使用ICMP重定向攻击将目标导向同一网络中不存在的另一台机器？即，\texttt{icmp.gw} 赋予的IP地址是一台本地已离线或不存在的计算机。
    请展示实验结果并解释你观察到的现象。

    \item 如果查看 \texttt{docker-compose.yml} 文件，你将找到恶意路由器容器的相关条目。这些条目的目的是什么？请将其值改为 \texttt{1} 并再次发起攻击。请描述和解释你观察到的现象。
    
\begin{lstlisting}
sysctls:
     - net.ipv4.conf.all.send_redirects=0
     - net.ipv4.conf.default.send_redirects=0
     - net.ipv4.conf.eth0.send_redirects=0
\end{lstlisting}

\end{itemize}


% -------------------------------------------
% SUBSECTION
% -------------------------------------------
\subsection{任务2.b：发起MITM攻击} 

使用ICMP重定向攻击，可以让受害者将我们的恶意路由器（\texttt{10.9.0.111}）作为目标 \texttt{192.168.60.5} 的路由器。因此，从受害者机器到此目的地的所有数据包都将通过恶意路由器路由。
我们希望修改受害者的数据包。

在发起MITM攻击之前，使用 \texttt{netcat} 启动一个TCP客户端和服务器程序。请查看以下命令：

\begin{lstlisting}
在目标容器 192.168.60.5 上启动 netcat 服务器：
# nc -lp 9090

在受害者容器中连接到该服务器:
# nc 192.168.60.5 9090
\end{lstlisting}

一旦建立了连接，你可以在受害机器上输入消息。每条消息将被放入一个TCP数据包发送到目标处，该目的地简单地显示了消息内容。
你的任务是将消息中的第一姓名序列替换为一系列 A 的序列，长度应与你的姓名相同。否则，会破坏TCP序号，并导致整个TCP连接出现问题。你需要使用真实的姓名。

\paragraph{禁用IP转发} 在此设置中，恶意路由器的 IP 转发功能已启用，因此它确实像一个路由器一样为其他机器转发数据包。在发起MITM攻击时，我们需要停止转发IP数据包；相反地，我们将拦截数据包、进行修改，并发送新的数据包。为此我们只需禁用恶意路由器上的IP转发。

\begin{lstlisting}
# sysctl net.ipv4.ip_forward=0
\end{lstlisting}

\paragraph{MITM代码} 在禁用了IP转发后，我们的程序需要接管从受害机器到目标的数据包转发任务；当然，在修改数据包之后。由于目的地不是我们，内核不会将此数据包交给我们，而是会直接丢弃它。然而，如果我们的程序是一个嗅探程序，则可以从内核中获取该数据包。因此我们将使用嗅探-伪造技术实现此MITM攻击。
以下提供了嗅探-伪造程序的骨架。此程序捕获所有TCP数据包，并进行一些修改。你需要对此程序进行修改。

\begin{lstlisting}[language=python]
#!/usr/bin/env python3
from scapy.all import *

def spoof_pkt(pkt):
   newpkt = IP(bytes(pkt[IP]))
   del(newpkt.chksum)
   del(newpkt[TCP].payload)
   del(newpkt[TCP].chksum)

   if pkt[TCP].payload:
       data = pkt[TCP].payload.load
       print("*** %s, length: %d" % (data, len(data)))

       # 替换一个模式
       newdata = data.replace(b'seedlabs', b'AAAAAAAA')

       send(newpkt/newdata)
   else:
       send(newpkt)

f = 'tcp'
pkt = sniff(iface='eth0', filter=f, prn=spoof_pkt)
\end{lstlisting}

需要注意的是，上述代码捕获了所有TCP数据包，包括程序本身生成的数据包。这会导致性能问题。学生需要更改过滤器，使其不捕获自己的数据包。

\paragraph{问题} 在成功实施攻击后，请回答以下问题：

\begin{itemize}
    \item 问题4：在你的MITM程序中，你只需要捕获一个方向上的流量，请指出哪个方向，并解释原因。
    
    \item 问题5：在MITM程序中，当你捕获来自A（\texttt{10.9.0.5}）的 \texttt{nc} 流量时，你可以使用A的IP地址或MAC地址来过滤。其中一项选择是不好的，并且即使这可能有效也会导致问题。
    请尝试两者并用你的实验结果展示哪个选项是正确的，请解释你的结论。
\end{itemize}

% *********************************************
% SECTION
% ********************************************* 
\section{提交作业}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input{\commonfolder/submission}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\end{document}
